package io.dcloud.uniplugin;

import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;

import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.facebook.appevents.AppEventsLogger;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Currency;
import java.util.Map;

import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.common.UniModule;

/**
 * @noinspection unused
 */
@Keep
public class FacebookModule extends UniModule {
    private static final String TAG = FacebookModule.class.getSimpleName();

    @Nullable
    public static AppEventsLogger appEventsLogger;

    /**
     * @noinspection DefaultAnnotationParam
     */
    @UniJSMethod(uiThread = true)
    public void logEventSimple(@NonNull String eventName) {
        try {
            ensureLogger();

            if (appEventsLogger != null) {
                appEventsLogger.logEvent(eventName);

                Log.i(TAG, "logEventSimple --- result:"
                        + "   \neventName:" + eventName
                );
            }
        } catch (Throwable throwable) {
            Log.e(TAG, "logEventSimple --- err:" + throwable.getMessage());
        }
    }

    /**
     * @noinspection DefaultAnnotationParam
     */
    @UniJSMethod(uiThread = true)
    public void logEventWithValue(@NonNull String eventName, @NonNull Double valueToSum) {
        try {
            ensureLogger();

            if (appEventsLogger != null) {
                appEventsLogger.logEvent(eventName, valueToSum);

                Log.i(TAG, "logEventWithValue --- result:"
                        + "   \neventName:" + eventName
                        + "   \nvalueToSum:" + valueToSum
                );
            }
        } catch (Throwable throwable) {
            Log.e(TAG, "logEventWithValue --- err:" + throwable.getMessage());
        }
    }

    /**
     * @noinspection DefaultAnnotationParam
     */
    @UniJSMethod(uiThread = true)
    public void logEventWithParams(@NonNull String eventName, @Nullable Map<String, Object> parameters) {
        try {
            ensureLogger();

            if (appEventsLogger != null) {
                final Bundle bundle = mapToBundle(parameters);
                appEventsLogger.logEvent(eventName, bundle);

                final String parametersStr = parameters != null ? parameters.toString() : null;
                final String bundleStr = bundle.toString();
                Log.i(TAG, "logEventWithParams --- result:"
                        + "   \neventName:" + eventName
                        + "   \nparameters:" + parametersStr
                        + "   \nbundle:" + bundleStr
                );
            }
        } catch (Throwable throwable) {
            Log.e(TAG, "logEventWithParams --- err:" + throwable.getMessage());
        }
    }

    /**
     * @noinspection DefaultAnnotationParam
     */
    @UniJSMethod(uiThread = true)
    public void logEventWithValueAndParams(
            @NonNull String eventName,
            @NonNull Double valueToSum,
            @Nullable Map<String, Object> parameters
    ) {
        try {
            ensureLogger();

            if (appEventsLogger != null) {
                final Bundle bundle = mapToBundle(parameters);
                appEventsLogger.logEvent(eventName, valueToSum, bundle);

                final String parametersStr = parameters != null ? parameters.toString() : null;
                final String bundleStr = bundle.toString();
                Log.i(TAG, "logEventWithValueAndParams --- result:"
                        + "   \neventName:" + eventName
                        + "   \nvalueToSum:" + valueToSum
                        + "   \nparameters:" + parametersStr
                        + "   \nbundle:" + bundleStr
                );
            }
        } catch (Throwable throwable) {
            Log.e(TAG, "logEventWithValueAndParams --- err:" + throwable.getMessage());
        }
    }

    /**
     * @noinspection DefaultAnnotationParam
     */
    @UniJSMethod(uiThread = true)
    public void logPurchase(
            @NonNull Double purchaseAmount,
            @NonNull String currencyCode,
            @Nullable Map<String, Object> parameters
    ) {
        try {
            ensureLogger();

            if (appEventsLogger != null) {
                final Bundle bundle = mapToBundle(parameters);
                appEventsLogger.logPurchase(BigDecimal.valueOf(purchaseAmount), Currency.getInstance(currencyCode), bundle);

                final String parametersStr = parameters != null ? parameters.toString() : null;
                final String bundleStr = bundle.toString();
                Log.i(TAG, "logPurchase --- result:"
                        + "   \npurchaseAmount:" + purchaseAmount
                        + "   \ncurrencyCode:" + currencyCode
                        + "   \nparameters:" + parametersStr
                        + "   \nbundle:" + bundleStr
                );
            }
        } catch (Throwable throwable) {
            Log.e(TAG, "logPurchase --- err:" + throwable.getMessage());
        }
    }

    /**
     * @noinspection DefaultAnnotationParam
     */
    @UniJSMethod(uiThread = true)
    public void logProductItem(
            @NonNull String id,
            @NonNull String availability,   // AppEventsLogger.ProductAvailability 枚举值的字符串
            @NonNull String condition,      // AppEventsLogger.ProductCondition 枚举值的字符串
            @Nullable String description,
            @Nullable String imageLink,
            @Nullable String link,
            @NonNull String title,
            @NonNull Double priceAmount,
            @NonNull String currencyCode,
            @Nullable String gtin,
            @Nullable String mpn,
            @Nullable String brand,
            @Nullable Map<String, Object> parameters
    ) {
        try {
            ensureLogger();

            if (appEventsLogger != null) {
                final AppEventsLogger.ProductAvailability availabilityEnum = AppEventsLogger.ProductAvailability.valueOf(availability);
                final AppEventsLogger.ProductCondition conditionEnum = AppEventsLogger.ProductCondition.valueOf(condition);
                final Bundle bundle = mapToBundle(parameters);

                appEventsLogger.logProductItem(
                        id,
                        availabilityEnum,
                        conditionEnum,
                        description,
                        imageLink,
                        link,
                        title,
                        BigDecimal.valueOf(priceAmount),
                        Currency.getInstance(currencyCode),
                        gtin,
                        mpn,
                        brand,
                        bundle
                );

                final String parametersStr = parameters != null ? parameters.toString() : null;
                final String bundleStr = bundle.toString();
                Log.i(TAG, "logProductItem --- result:"
                        + "   \nid:" + id
                        + "   \navailability:" + availability
                        + "   \ncondition:" + condition
                        + "   \ndescription:" + description
                        + "   \nimageLink:" + imageLink
                        + "   \nlink:" + link
                        + "   \ntitle:" + title
                        + "   \npriceAmount:" + priceAmount
                        + "   \ncurrencyCode:" + currencyCode
                        + "   \ngtin:" + gtin
                        + "   \nmpn:" + mpn
                        + "   \nbrand:" + brand
                        + "   \nparameters:" + parameters
                        + "   \nbundleStr:" + bundleStr
                );
            }
        } catch (Throwable throwable) {
            Log.e(TAG, "logProductItem --- err:" + throwable.getMessage());
        }
    }

    private void ensureLogger() {
        if (appEventsLogger == null && mUniSDKInstance != null) {
            final Context context = mUniSDKInstance.getContext();
            if (context != null) {
                appEventsLogger = AppEventsLogger.newLogger(context);
            }
        }
    }

    /**
     * @noinspection ConstantValue, unchecked
     */
    @NonNull
    private static Bundle mapToBundle(@Nullable Map<String, Object> map) {
        final Bundle bundle = new Bundle();
        if (map == null) return bundle;

        for (Map.Entry<String, Object> entry : map.entrySet()) {
            final String key = entry.getKey();
            final Object value = entry.getValue();

            if (value == null) continue;

            if (value instanceof String) {
                bundle.putString(key, (String) value);
            } else if (value instanceof Number) {
                // 所有数字统一转 double
                bundle.putDouble(key, ((Number) value).doubleValue());
            } else if (value instanceof Boolean) {
                bundle.putBoolean(key, (Boolean) value);
            } else if (value instanceof Bundle) {
                bundle.putBundle(key, (Bundle) value);
            } else if (value instanceof Serializable) {
                bundle.putSerializable(key, (Serializable) value);
            } else if (value instanceof Parcelable) {
                bundle.putParcelable(key, (Parcelable) value);
            } else if (value instanceof String[]) {
                bundle.putStringArray(key, (String[]) value);
            } else if (value instanceof int[]) {
                bundle.putIntArray(key, (int[]) value);
            } else if (value instanceof long[]) {
                bundle.putLongArray(key, (long[]) value);
            } else if (value instanceof boolean[]) {
                bundle.putBooleanArray(key, (boolean[]) value);
            } else if (value instanceof float[]) {
                bundle.putFloatArray(key, (float[]) value);
            } else if (value instanceof double[]) {
                bundle.putDoubleArray(key, (double[]) value);
            } else if (value instanceof ArrayList) {
                final ArrayList<?> list = (ArrayList<?>) value;
                if (list.isEmpty()) {
                    bundle.putStringArrayList(key, new ArrayList<>());
                } else {
                    final Object first = list.get(0);
                    if (first instanceof String) {
                        bundle.putStringArrayList(key, (ArrayList<String>) list);
                    } else if (first instanceof Integer) {
                        bundle.putIntegerArrayList(key, (ArrayList<Integer>) list);
                    } else if (first instanceof Parcelable) {
                        bundle.putParcelableArrayList(key, (ArrayList<? extends Parcelable>) list);
                    }
                }
            }
        }
        return bundle;
    }
}